package 代码随想录_动态规划.买卖股票;

/**
 * @author zx
 * @create 2022-06-02 17:04
 * 组成部分一：确定状态
 *               最后一步：
 *               子问题：
 *               确定dp数组(dp table)以及下标的含义
 *               dp[i][j]：第i天的状态为j,所剩下的最大现金是dp[i][j]
 *               j的状态表示为：
 *               0 表示不操作
 *               1 第一次买入
 *               2 第一次卖出
 *               3 第二次买入
 *               4 第二次卖出
 *               规律：除了0以外,偶数就是卖出,奇数就是买入
 *               题目要求是至多有K笔交易,那么j的范围就定义为 2 * k + 1
 * 组成部分一：确定状态
 * 最后一步：
 * 子问题：
 * 组成部分二：转移方程
 * 强调一下：dp[i][1],表示的是第i天,买入股票的状态,并不是说一定要第i天买入股票,容易陷入的误区
 * 达到dp[i][1]状态，有两个具体操作：
 * 操作一：第i天买入股票了,那么dp[i][1] = dp[i - 1][0] - prices[i]
 * 操作二：第i天没有操作,而是沿用前一天买入的状态,即：dp[i][1] = dp[i - 1][1]
 * 选最大的,所以 dp[i][1] = max(dp[i - 1][0] - prices[i], dp[i - 1][0]);
 * 同理dp[i][2]也有两个操作：
 * 操作一：第i天卖出股票了,那么dp[i][2] = dp[i - 1][1] + prices[i]
 * 操作二：第i天没有操作,沿用前一天卖出股票的状态,即：dp[i][2] = dp[i - 1][2]
 * 所以dp[i][2] = max(dp[i - 1][i] + prices[i], dp[i][2])
 * ##########本题和123.买卖股票的最佳时机III最大的区别就是这里要类比j为偶数是买、奇数是卖的状态#########################
 *
 * 组成部分三：初始条件和边界情况
 * 第0天没有操作,这个最容易想到,就是0,即：dp[0][0] = 0;
 * 第0天做第一次买入的操作,dp[0][1] = -prices[0];
 * 第0天做第一次卖出的操作,这个初始值应该是多少呢？
 * 首先卖出的操作一定是收获利润,整个股票买卖最差情况也就是没有盈利即全程无操作现金为0，
 * 从递推公式中可以看出每次是取最大值,那么既然是收获利润如果比0还小就没有必要收获这个利润了。
 * 所以dp[0][2] = 0;
 * 第0天第二次买入操作,初始值应该是多少呢？
 * 不用管第几次,现在手头上没有现金,只要买入,现金就做相应的减少
 * 第二次买入操作,初始化为：dp[0][3] = -prices[0];
 * 所以同理可以推出dp[0][j]当j为奇数的时候都初始化为 -prices[0]
 * 组成部分四：计算顺序
 * 最后一次卖出,一定是利润最大的,dp[prices.size() - 1][2 * k]
 */
public class 买卖股票的最佳时机IV_188 {
    /**
     * @return 此题类比于123
     *
     * 规律：除0以外,偶数就是卖出,奇数就是买入
     */
    public int maxProfit(int k, int[] prices) {
        int[][] dp = new int[prices.length][2 * k + 1];
        if(prices.length == 0){
            return 0;
        }
        for(int i = 1;i < 2 * k + 1;i += 2){
            dp[0][i] = -prices[0];
            dp[0][i + 1] = 0;
        }
        for(int i = 1;i < prices.length;i++){
            for(int j = 1;j < 2 * k;j += 2){
                dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i]);
                dp[i][j + 1] = Math.max(dp[i - 1][j + 1], dp[i - 1][j] + prices[i]);
            }
        }
        //最后一次卖出,一定是利润最大的,dp[prices.size() - 1][2 * k]
        return dp[prices.length - 1][2 * k];
    }
}
